package com.maimao.search.utils;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.ReflectUtil;
import cn.hutool.core.util.StrUtil;
import com.google.common.collect.LinkedHashMultimap;
import com.google.common.collect.Maps;
import com.maimao.core.enums.ExceptionEnum;
import com.maimao.core.exception.MaiMaoException;
import com.maimao.model.es.EsSearchConvert;
import com.maimao.model.es.anno.EsSearchFiled;
import lombok.extern.slf4j.Slf4j;
import org.apache.lucene.search.join.ScoreMode;
import org.elasticsearch.index.query.*;
import org.springframework.stereotype.Component;
import org.springframework.util.ReflectionUtils;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Collection;
import java.util.Map;
import java.util.Objects;
import java.util.Set;

import static com.maimao.core.constant.Constants.*;

/**
 * @author MaoLin Wang
 * @date 2021/4/27 下午5:19
 */
@Slf4j
@Component
public class EsQueryFieldHandler {

    public static final String TOTAL = "total";
    public static final String MAX = "max";
    public static final String MIN = "min";
    public static final String NONE = "none";
    public static final String AVG = "avg";

    /**
     * 根据查询条件对象封装布尔查询器
     *
     * @param searchInput 查询参数
     * @return BoolQueryBuilder
     */
    public BoolQueryBuilder handle(Object searchInput) {
        BoolQueryBuilder baseQueryBuilder = QueryBuilders.boolQuery();
        /**
         * key:范围查询的字段名 value:该范围查询字段的两个取值
         */
        final LinkedHashMultimap<String, Object> multimap = LinkedHashMultimap.create();

        /**
         * key:范围查询的字段名 value:嵌套查询字段相关信息
         */
        final Map<String, RangeQueryFiled> rangeIsNestedMap = Maps.newHashMap();

        for (Field field : searchInput.getClass().getDeclaredFields()) {
            EsSearchFiled searchFiled = field.getAnnotation(EsSearchFiled.class);
            if (null != searchFiled) {
                try {
                    field.setAccessible(true);
                    Object value = field.get(searchInput);
                    if (ObjectUtil.isEmpty(value)&&!searchFiled.isRangeQuery()) {
                        continue;
                    }
                    if (searchFiled.nested()) {
                        // 嵌套查询
                        if (StrUtil.isEmpty(searchFiled.nestedPath())) {
                            log.error("es嵌套查询path为空，查询字段：{}", field.getName());
                            throw new MaiMaoException(ExceptionEnum.ES_NESTED_QUERY_ERROR);
                        }
                        if (searchFiled.nestedBool()) {
                            //嵌套布尔查询
                            BoolQueryBuilder nestedBoolQuery = QueryBuilders.boolQuery();
                            QueryBuilder queryBuilder = doHandleBoolQuery(searchFiled, value, multimap);
                            if (null != queryBuilder) {
                                nestedBoolQuery.must(queryBuilder);
                            }
                            baseQueryBuilder.must(QueryBuilders.nestedQuery(
                                    searchFiled.nestedPath(), nestedBoolQuery, getScoreMode(searchFiled.nestedScoreMode().toLowerCase()))
                            );

                        } else {
                            QueryBuilder queryBuilder = doHandleBoolQuery(searchFiled, value, multimap);
                            if (null != queryBuilder) {
                                baseQueryBuilder.must(QueryBuilders.nestedQuery(searchFiled.nestedPath(), queryBuilder, getScoreMode(searchFiled.nestedScoreMode().toLowerCase())));
                            } else {
                                // 嵌套查询中是范围查询
                                rangeIsNestedMap.put(searchFiled.fieldName(), new RangeQueryFiled()
                                        .setFieldName(searchFiled.fieldName())
                                        .setNested(true)
                                        .setPath(searchFiled.nestedPath())
                                        .setScoreMode(getScoreMode(searchFiled.nestedScoreMode().toLowerCase())));
                            }
                        }

                    } else {
                        //非嵌套
                        QueryBuilder queryBuilder = doHandleBoolQuery(searchFiled, value, multimap);
                        if (null != queryBuilder) {
                            baseQueryBuilder.must(queryBuilder);
                        } else {
                            rangeIsNestedMap.put(searchFiled.fieldName(), new RangeQueryFiled()
                                    .setNested(false));
                        }
                    }

                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                    log.error("反射获取字段值失败，字段:{}", field);
                }
            }
        }

        if (!multimap.isEmpty()) {
            // 处理范围查询
            for (Map.Entry<String, Collection<Object>> entry : multimap.asMap().entrySet()) {
                String fieldName = entry.getKey();
                // 范围查询两个值
                Object[] rangeValues = entry.getValue().toArray(new Object[ZERO]);
                if (rangeValues.length == TWO) {
                    Object leftValue = rangeValues[ZERO];
                    Object rightValue = rangeValues[ONE];
                    if (null != leftValue || null != rightValue) {
                        RangeQueryFiled nestedQueryFiled = rangeIsNestedMap.get(fieldName);
                        if (nestedQueryFiled != null) {
                            RangeQueryBuilder rangeQueryBuilder = getRangeQueryBuilder(nestedQueryFiled, fieldName, leftValue, rightValue, nestedQueryFiled);
                            if (nestedQueryFiled.isNested()) {
                                // 如果是嵌套查询
                                baseQueryBuilder.must(
                                        QueryBuilders.nestedQuery(
                                                nestedQueryFiled.getPath(),
                                                rangeQueryBuilder,
                                                nestedQueryFiled.getScoreMode()
                                        )
                                );
                            } else {
                                // 非嵌套查询
                                baseQueryBuilder.must(rangeQueryBuilder);
                            }

                        }
                    }
                }

            }

        }
        return baseQueryBuilder;

    }

    /**
     * 根据条件获取范围查询
     *
     * @param queryFiled
     * @param fieldName        字段名
     * @param leftValue        左值
     * @param rightValue       右值
     * @param nestedQueryFiled 范围查询字段信息
     * @return RangeQueryBuilder
     */
    private RangeQueryBuilder getRangeQueryBuilder(RangeQueryFiled queryFiled, String fieldName, Object leftValue, Object rightValue, RangeQueryFiled nestedQueryFiled) {
        RangeQueryBuilder rangeQueryBuilder = QueryBuilders.rangeQuery(queryFiled.isNested() ? queryFiled.getPath() + fieldName : fieldName)
                .gte(leftValue)
                .lte(rightValue);
        if (nestedQueryFiled.isLeftOpen()) {
            rangeQueryBuilder.gt(leftValue);
        }
        if (nestedQueryFiled.isRightOpen()) {
            rangeQueryBuilder.lt(rightValue);
        }
        return rangeQueryBuilder;
    }

    /**
     * 布尔查询处理
     *
     * @param searchFiled 注解
     * @param value       字段值
     * @param multimap    存放范围查询的多值map
     */
    private QueryBuilder doHandleBoolQuery(EsSearchFiled searchFiled, Object value, LinkedHashMultimap<String, Object> multimap) {

        if (!searchFiled.isRangeQuery()) {
            String fieldPrefix = EMPTY;
            if (searchFiled.nested()) {
                //嵌套查询需要加上path.前缀
                fieldPrefix = searchFiled.nestedPath() + DOT;
            }
            if (searchFiled.matchQuery()) {
                //匹配查询
                MatchQueryBuilder matchQueryBuilder = QueryBuilders.matchQuery(fieldPrefix + searchFiled.fieldName(), value);
                if (searchFiled.matchOperatorAnd()) {
                    matchQueryBuilder.operator(Operator.AND);
                }
                return matchQueryBuilder;
            } else if (value instanceof Collection) {
                if (CollectionUtil.isNotEmpty((Collection<?>) value)) {
                    if (((Collection<?>) value).stream().anyMatch(Objects::nonNull)) {
                        return QueryBuilders.termsQuery(fieldPrefix + searchFiled.fieldName(), (Collection<?>) value);
                    }
                }
            } else {
                // 普通term查询
                searchFiled.valueConvert();
                // 处理值转换
                Class<? extends EsSearchConvert> valueConvert = searchFiled.valueConvert();
                if (value instanceof EsSearchConvert.DefaultEsSearchConvert) {
                    //默认转换器不处理

                } else {
                    Object convertValue = ReflectUtil.invoke(ReflectUtil.newInstance(valueConvert), EsSearchConvert.METHOD_NAME, value);
                    if (null != convertValue) {
                        value = convertValue;
                    }
                }

                return QueryBuilders.termQuery(fieldPrefix + searchFiled.fieldName(), value);
            }
        } else {
            multimap.put(searchFiled.fieldName(), value);
        }
        return null;
    }

    private ScoreMode getScoreMode(String nestedScoreMode) {
        switch (nestedScoreMode) {
            case TOTAL:
                return ScoreMode.Total;
            case MAX:
                return ScoreMode.Max;
            case MIN:
                return ScoreMode.Min;
            case NONE:
                return ScoreMode.None;
            case AVG:
            default:
                return ScoreMode.Avg;
        }
    }


}
